home *** CD-ROM | disk | FTP | other *** search
/ Risc World 3 / Risc World 3.iso / SOFTWARE / ISSUE6 / PD / PDF / pdf / c++ / outline < prev    next >
Text File  |  2003-02-14  |  16KB  |  599 lines

  1. //--------------------------------------------------------------------------
  2. //
  3. //   Copyright (c) 2002, Colin Granville
  4. //
  5. //   All rights reserved.
  6. //
  7. //   Redistribution and use in source and binary forms, with or
  8. //   without modification, are permitted provided that the following 
  9. //   conditions are met:
  10. //
  11. //      * Redistributions of source code must retain the above copyright 
  12. //        notice, this list of conditions and the following disclaimer.
  13. //
  14. //      * Redistributions in binary form must reproduce the above 
  15. //        copyright notice, this list of conditions and the following 
  16. //        disclaimer in the documentation and/or other materials 
  17. //        provided with the distribution.
  18. //
  19. //      * The name Colin Granville may not be used to endorse or promote 
  20. //        products derived from this software without specific prior 
  21. //        written permission.
  22. //
  23. //   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
  24. //   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
  25. //   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
  26. //   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
  27. //   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
  28. //   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
  29. //   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
  30. //   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
  31. //   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
  32. //   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  33. //   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
  34. //   OF THE POSSIBILITY OF SUCH DAMAGE.
  35. //
  36. //--------------------------------------------------------------------------
  37.  
  38. #include "guilib:gfx.h"
  39. #include "outline.h"
  40. #include "PDFDoc.h"
  41. #include "Catalog.h"
  42. #include "iostream.h"
  43. #include <stdlib.h>
  44. #include <stdarg.h>
  45. #include "object.h"
  46. #include "xref.h"
  47. #include "link.h"
  48. #include "stl:string.h"
  49. #include "DocView.h"
  50. #include "GuiIcon.h"
  51. #include "GuiTask.h"
  52. #include "Document.h"
  53. #include "GuiHourglass.h"
  54. #include "DrawOutputFont.h"
  55.  
  56. #define SPRITEOUTDENT 28
  57. inline int levelIndent(int level) {return level*52;}
  58.  
  59.  
  60. LinkAction* makeAction(Dict *dict, GString *baseURI);
  61.  
  62. class OutlineFont
  63. {
  64.   public:
  65.     OutlineFont();
  66.     ~OutlineFont();
  67.     bool isOk()        {return handle;}
  68.   private:
  69.     int handle;
  70. };
  71.  
  72. //******************************************************************************
  73.  
  74. OutlineFont::OutlineFont()
  75.   : handle(0)
  76. {
  77.   _swix(Font_FindFont,_INR(1,5)|_OUT(0),"Homerton.Medium",10*16,10*16,0,0,&handle);
  78. }
  79.  
  80. //******************************************************************************
  81.  
  82. OutlineFont::~OutlineFont()
  83. {
  84.   if (handle)  _swix(Font_LoseFont,_IN(0),handle);
  85. }
  86.  
  87. //******************************************************************************
  88. //******************************************************************************
  89. //******************************************************************************
  90.  
  91. class OutlineNode
  92. {
  93.   public:
  94.     OutlineNode(OutlineNode* parent_,Object* o);
  95.     ~OutlineNode();
  96.     OutlineNode*   getParent()                    {return parent;}
  97.     void           setParent(OutlineNode* n)      {parent=n;}
  98.     OutlineNode*   getNext()                      {return next;}
  99.     void           setNext(OutlineNode* n)        {next=n;}
  100.     OutlineNode*   getFirst()                     {return first;}
  101.     void           setFirst(OutlineNode* n)       {first=n;}
  102.     int            getCount()                     {return count;}
  103.     void           setCount(int n)                {count=n;}
  104.     const string&  getText() const                {return text;}
  105.     Object*        getObject()                    {return ob;}
  106.  
  107.  
  108.   private:
  109.     OutlineNode* parent;
  110.     OutlineNode* first;
  111.     OutlineNode* next;
  112.     int          count;
  113.     Object*      ob;
  114.     string       text;
  115. };
  116.  
  117. OutlineNode::OutlineNode(OutlineNode* parent_,Object* o)
  118.  : parent(parent_),
  119.    first(0),
  120.    next(0),
  121.    count(0),
  122.    ob(o)
  123. {
  124.    Object t;
  125.    o->dictLookup("Title",&t);
  126.    if (t.isString()) text=toAcornLatin1(t.getString()->getCString(),
  127.                                         t.getString()->getLength());
  128.  
  129.    t.free();
  130. }
  131.  
  132. //******************************************************************************
  133.  
  134. OutlineNode::~OutlineNode()
  135. {
  136.   delete first;
  137.   delete next;
  138.   if (ob) ob->free();
  139.   delete ob;
  140. }
  141.  
  142. //******************************************************************************
  143. //******************************************************************************
  144. //******************************************************************************
  145.  
  146. Outline::Outline(PDFDoc& doc)
  147.   : window("Outline"),
  148.     owner(0),
  149.     root(0),
  150.     pdfDoc(0),
  151.     height(28),
  152.     redrawWindowTarget(&window,GuiWimp_ERedrawWindow,this,Outline::redrawWindow),
  153.     mouseClickTarget(&window,GuiWimp_EMouseClick,this,Outline::mouseClick)
  154. {
  155.   if (!doc.getXRef()) return;
  156.  
  157.   Object catalog;
  158.   doc.getXRef()->getCatalog(&catalog);
  159.   if (!catalog.isDict()) {catalog.free();return;}
  160.  
  161.   Object outlines;
  162.   catalog.dictLookup("Outlines",&outlines);
  163.   if (outlines.isDict())
  164.   {
  165.     Object ob;
  166.     int count=0;
  167.     outlines.dictLookup("First",&ob);
  168.     if (ob.isDict()) count++;
  169.     ob.free();
  170.     outlines.dictLookup("Next",&ob);
  171.     if (ob.isDict()) count++;
  172.     ob.free();
  173.     if (count>0) pdfDoc=&doc;
  174.   } 
  175.   outlines.free();
  176.   catalog.free();
  177. }
  178.  
  179.  
  180. //******************************************************************************
  181.  
  182. Outline::~Outline()
  183. {
  184.   delete root;
  185. }
  186.  
  187. //******************************************************************************
  188.  
  189. OutlineNode* Outline::fillTree(OutlineNode*parent,Object* entry )
  190. {
  191.   if (!entry) return 0;
  192.   OutlineNode* node = new OutlineNode(parent,entry);
  193.   if (!node) return 0;
  194.   
  195.   Object* nextOb=new Object;
  196.   if (nextOb)
  197.   {
  198.     entry->dictLookup("Next",nextOb);
  199.     if (!nextOb->isDict())
  200.     {
  201.       nextOb->free();
  202.       delete nextOb;
  203.       nextOb=0;
  204.     }
  205.   }
  206.  
  207.   Object* firstOb=new Object;
  208.   if (firstOb)
  209.   {
  210.     entry->dictLookup("First",firstOb);
  211.     if (!firstOb->isDict())
  212.     {
  213.       firstOb->free();
  214.       delete firstOb;
  215.       firstOb=0;
  216.     }
  217.   }
  218.   
  219.   int count=0;
  220.   {
  221.     Object ob;
  222.     entry->dictLookup("Count",&ob);
  223.     if (ob.isInt()) count=ob.getInt();
  224.     ob.free();
  225.   }
  226.  
  227.   node->setCount(count);
  228.   node->setFirst(fillTree(node,firstOb));
  229.   node->setNext(fillTree(parent,nextOb));
  230.   return node;
  231. }
  232.  
  233. //******************************************************************************
  234.  
  235. // Font must be set before use
  236. bool Outline::print(OutlineNode* node,int level)
  237. {
  238.   while (node) 
  239.   {
  240.     if (node->getText().size())
  241.     {
  242.       basey-=height;
  243.       int y=redrawBlock->yToScreen(basey);
  244.       if (y+height <= redrawBlock->redrawArea.ymin) return 1;
  245.       if (y < redrawBlock->redrawArea.ymax)
  246.       {
  247.          OutlineNode*n=node;
  248.          gfx::gcol_bgr(0,0xdadada00);
  249.          int x=redrawBlock->xToScreen(basex);
  250.          gfx::move(x+levelIndent(level)-SPRITEOUTDENT+8,y+height/2);
  251.          gfx::drawby(SPRITEOUTDENT-12,0);
  252.          for (int i=level;i>0;i--,n=n->getParent())
  253.          {
  254.            int extra=(n->getNext()?0:height/2);
  255.            if (extra && i!=level) continue;
  256.            gfx::move(x+levelIndent(i)-SPRITEOUTDENT+8,y+extra);
  257.            gfx::drawby(0,height-extra);
  258.          }
  259.  
  260.          int left=basex+levelIndent(level);
  261.          if (node->getCount())
  262.          {
  263.            GuiIcon icon;
  264.            icon.flags = GuiIcon::Sprite |
  265.                          GuiIcon::Indirected;
  266.  
  267.  
  268.            icon.data.is.spriteNameLength = 12;
  269.            icon.data.is.spriteArea = (void*) guiTask().spriteArea();
  270.            icon.data.is.sprite = (node->getCount()<0  ? "plus":"minus");
  271.            icon.bbox(left-SPRITEOUTDENT,basey+height/4,left,basey+height+height/4);
  272.            icon.plot();
  273.  
  274.          }
  275.  
  276.          _swix(ColourTrans_SetFontColours,_INR(0,3),0,0xFFFFFF00,0,14);
  277.          _swix(Font_Paint,_INR(0,7),0,node->getText().c_str(),0,
  278.                                     redrawBlock->xToScreen(left)*400,y*400+height*400*1/4,0,0,0);
  279.       }
  280.     }
  281.  
  282.     if (node->getCount() > 0) if (print(node->getFirst(),level+1)) return 1;
  283.     node=node->getNext();
  284.   }
  285.   return 0;
  286. }
  287.  
  288. //******************************************************************************
  289.  
  290. bool Outline::find(OutlineNode* node,int level)
  291. {
  292.   for (;node;node=node->getNext()) 
  293.   {
  294.     if (node->getText().size())
  295.     {
  296.       basey-=height;
  297.       if (findy >=basey && findy < basey+height )
  298.       {    
  299.         foundNode=node;
  300.         foundLevel=level;
  301.         return 1;
  302.       }
  303.     }
  304.  
  305.     if (node->getCount() > 0 && find(node->getFirst(),level+1)) return 1;
  306.   }
  307.   return 0;
  308. }
  309.  
  310. //******************************************************************************
  311.  
  312. int Outline::countLines(OutlineNode* node)
  313. {
  314.   int count=0;
  315.   for (;node;node=node->getNext()) 
  316.   {
  317.     if (node->getText().size()) count++;
  318.     if (node->getCount() > 0) count+=countLines(node->getFirst());
  319.   }
  320.   return count;
  321. }
  322.  
  323. //******************************************************************************
  324.  
  325. // returned width is in millipoints
  326. // Font must be set before use
  327. int getMaxWidth(OutlineNode* node,int level=0)
  328. {
  329.   int width=0;
  330.   for (;node;node=node->getNext()) 
  331.   {
  332.     if (node->getText().size())
  333.     {
  334.       int wid;
  335.       _swix(Font_ScanString,_INR(1,4) | _OUT(3),node->getText().c_str(),0,0x80000000,0x80000000,&wid);
  336.       wid+=(levelIndent(level)*400);
  337.       if (wid>width) width=wid;
  338.     }
  339.  
  340.     if (node->getCount() != 0)
  341.     {
  342.       int wid=getMaxWidth(node->getFirst(),level+1);
  343.       if (wid>width) width=wid;
  344.     }
  345.   }
  346.   return width;
  347. }
  348.  
  349. //******************************************************************************
  350.  
  351. void Outline::open(DocView& view)
  352. {
  353.   if (owner!=&view) close(view);
  354.   owner=&view;
  355.  
  356.   if (!isOk()) return;
  357.  
  358.   if (!root)
  359.   {
  360.     Object catalog;
  361.     pdfDoc->getXRef()->getCatalog(&catalog);
  362.     if (!catalog.isDict()) {catalog.free();return;}
  363.  
  364.     Object* outlines=new Object;
  365.     if (outlines)
  366.     {
  367.       catalog.dictLookup("Outlines",outlines);
  368.       if (!outlines->isDict())
  369.       {
  370.         outlines->free();
  371.         delete outlines;
  372.         outlines=0;
  373.       }
  374.     }
  375.     catalog.free();
  376.  
  377.     {
  378.       GuiHourglass hg;
  379.       root=fillTree(0,outlines);
  380.     }
  381.  
  382.     if (!root)
  383.     {
  384.       if (outlines) outlines->free();
  385.       delete outlines;
  386.     }
  387.     else
  388.     {
  389.        GuiBBox box;
  390.        window.getExtent(box);
  391.        int ext=countLines(root)*height;
  392.        if (ext==0)
  393.        {
  394.          delete root;
  395.          root=0;
  396.        }
  397.        else
  398.        {
  399.          box.ymin=-ext;
  400.          OutlineFont font;
  401.          box.xmax=(getMaxWidth(root)/400)+16;
  402.          window.setExtent(box);
  403.        }
  404.     }
  405.     if (!root) pdfDoc=0;
  406.   }
  407.   if (isOk()) window.showAsMenuAtPointer(view.getWindow());
  408. }
  409.  
  410. //******************************************************************************
  411.  
  412. void Outline::close(DocView& view)
  413. {
  414.   if (owner==&view)
  415.   {
  416.     window.hide();
  417.     owner=0;
  418.   }
  419. }
  420.  
  421. //******************************************************************************
  422.  
  423. Claim Outline::redrawWindow(GuiWimpPollBlock& wpb,const GuiIdBlock&)
  424. {
  425.   bool more;
  426.   GuiRedrawWindowBlock& block=wpb.redrawWindowRequest;
  427.  
  428.   for (GuiWindow::redraw(block,more);more;GuiWindow::getRectangle(block,more))
  429.   {
  430.     basey=0;
  431.     basex=0;
  432.     redrawBlock=█
  433.     OutlineFont font;
  434.     if (font.isOk()) print(root);
  435.   }
  436.  
  437.   return CLAIM;
  438. }
  439.  
  440. //*************************************************************************
  441.  
  442. Claim Outline::mouseClick(GuiWimpPollBlock& wpb,const GuiIdBlock&)
  443. {
  444.   if (wpb.mouseClick.buttons & GuiPointerInfo::Menu) return CLAIM;
  445.  
  446.   GuiGetWindowStateBlock ws;
  447.   window.getState(ws);
  448.   int x=ws.xToWorkarea(wpb.mouseClick.x);
  449.  
  450.   findy=ws.yToWorkarea(wpb.mouseClick.y);
  451.   basey=0;
  452.   if (find(root))
  453.   {
  454.     if (foundNode->getCount())
  455.     {
  456.       int x=ws.xToWorkarea(wpb.mouseClick.x);
  457.       if (x >= levelIndent(foundLevel)-SPRITEOUTDENT-4 && 
  458.           x < levelIndent(foundLevel))
  459.       {
  460.         foundNode->setCount(-foundNode->getCount());
  461.  
  462.         if (wpb.mouseClick.buttons & GuiPointerInfo::Adjust)
  463.         {
  464.           int close=(foundNode->getCount()<0);
  465.           while (foundNode->getNext())
  466.           {
  467.              foundNode=foundNode->getNext();
  468.              if ((close && foundNode->getCount()>0) ||
  469.                  (!close && foundNode->getCount()<0)
  470.                 ) foundNode->setCount(-foundNode->getCount());
  471.                 
  472.           }
  473.         }
  474.  
  475.         GuiBBox box;
  476.         window.getExtent(box);
  477.         int ext=countLines(root)*height;
  478.         if (ext<ws.visibleArea.getHeight()) ext=ws.visibleArea.getHeight();
  479.         box.ymin=-ext;
  480.         window.setExtent(box);
  481.         int bottom=-ws.yToWorkarea(ws.visibleArea.ymin);
  482.         if (bottom>ext)
  483.         {
  484.           ws.yscroll+=(bottom-ext);
  485.           window.showAsMenu(ws,owner->getWindow());
  486.         }
  487.  
  488.         box.ymax=basey+height;        
  489.         window.forceRedraw(box);
  490.         return CLAIM;
  491.       }
  492.     }
  493.  
  494.     { // limit clicks to text
  495.       int wid=0;
  496.       OutlineFont font;
  497.       if (font.isOk()) _swix(Font_ScanString,_INR(0,5) | _OUT(3), 0,foundNode->getText().c_str(),0,
  498.                        -1,-1,0,&wid);
  499.       if (x < levelIndent(foundLevel) || x > (levelIndent(foundLevel)+wid/400))
  500.       {
  501.         if (owner) close(*owner);
  502.         return CLAIM;
  503.       }
  504.     }
  505.  
  506.     if (owner)
  507.     {
  508.       LinkAction* action=makeAction(foundNode->getObject()->getDict(),
  509.                                     owner->getDocument().getCatalog()->getBaseURI());
  510.  
  511.       if (action)
  512.       {
  513.          owner->doAction(action,wpb.mouseClick.buttons & GuiPointerInfo::Adjust);
  514.          delete action;
  515.       }
  516.       close(*owner);
  517.     }
  518.   }
  519.   else
  520.   {
  521.     if (owner) close(*owner);
  522.   }
  523.   return CLAIM;
  524. }
  525.  
  526. //******************************************************************************
  527. //******************************************************************************
  528. //******************************************************************************
  529.  
  530. LinkAction* makeAction(Dict *dict, GString *baseURI)
  531. {
  532.   Object obj1;
  533.   LinkAction* action=0;
  534.  
  535.   // look for destination
  536.   if (!dict->lookup("Dest", &obj1)->isNull()) {
  537.     action = new LinkGoTo(&obj1);
  538.  
  539.   // look for action
  540.   } else {
  541.     Object obj2;
  542.     obj1.free();
  543.     if (dict->lookup("A", &obj1)->isDict()) {
  544.       obj1.dictLookup("S", &obj2);
  545.       Object obj3,obj4;
  546.  
  547.       // GoTo action
  548.       if (obj2.isName("GoTo")) {
  549.         obj1.dictLookup("D", &obj3);
  550.         action = new LinkGoTo(&obj3);
  551.         obj3.free();
  552.  
  553.       // GoToR action
  554.       } else if (obj2.isName("GoToR")) {
  555.         obj1.dictLookup("F", &obj3);
  556.         obj1.dictLookup("D", &obj4);
  557.         action = new LinkGoToR(&obj3, &obj4);
  558.         obj3.free();
  559.         obj4.free();
  560.  
  561.       // Launch action
  562.       } else if (obj2.isName("Launch")) {
  563.         action = new LinkLaunch(&obj1);
  564.  
  565.       // URI action
  566.       } else if (obj2.isName("URI")) {
  567.         obj1.dictLookup("URI", &obj3);
  568.         action = new LinkURI(&obj3, baseURI);
  569.         obj3.free();
  570.  
  571.       // Named action
  572.       } else if (obj2.isName("Named")) {
  573.         obj1.dictLookup("N", &obj3);
  574.         action = new LinkNamed(&obj3);
  575.         obj3.free();
  576.  
  577.       // unknown action
  578.       } else if (obj2.isName()) {
  579.         action = new LinkUnknown(obj2.getName());
  580.  
  581.       // action is missing or wrong type
  582.       } else {
  583.         //error(-1, "Bad annotation action");
  584.         action = NULL;
  585.       }
  586.  
  587.       obj2.free();
  588.  
  589.     }
  590.   }
  591.   obj1.free();
  592.   return action;
  593. }
  594.  
  595. //****************************************************************
  596. //****************************************************************
  597. //****************************************************************
  598.  
  599.